#include"../include/head.h"
#include<my_header.h>

int send_TLVLV_req(RequestPackage *prequst,const int socked_fd);//此函数用于发送TLVLV请求包
int recv_TLVLV_req(ResponsePackage *presponse,const int socked_fd);//此函数用于接收TLVLV响应包

int client_puts( ResponsePackage *presponse, ClientVirtualPathStack* pvirtualpathstack,int socked_fd){
    
    char* filename = (char*)calloc(256,1);

    char *addr1=strtok(presponse->args_val," ");//客户端文件地址

    int index=-1;
    for(int i=strlen(addr1)-1;i>=0;i--){//用此循环得到最后一个/的位置
        if(addr1[i]=='/'){
            index=i;
            break;
        }
    }
    if(index!=-1)
    {
        memcpy(filename, &addr1[index+1], strlen(addr1)-index-1);//修复：添加&取地址操作符
    }
    else
    {
        index=0;
        memcpy(filename, &addr1[index], strlen(addr1)-index);//修复：添加&取地址操作符
    }
    int filename_len=strlen(filename);
    // printf("addr1:%s filename:%s filename_len:%d\n",addr1,filename,filename_len);

    //发送文件名长度与文件名
    send(socked_fd,&filename_len,sizeof(int),MSG_NOSIGNAL);
    // printf("send filename_len end, %d\n",filename_len);
    send(socked_fd,filename,filename_len,MSG_NOSIGNAL);
    // printf("send filename end, %s\n",filename);

    //发送filesize
    struct stat file_stat;
    if (stat(addr1, &file_stat) == -1) {
        printf("stat error\n");
        return -1;
    }
    off_t filesize = file_stat.st_size;
    send(socked_fd,&filesize,sizeof(off_t),MSG_NOSIGNAL);
    // printf("send filesize end , %ld\n",filesize);

    //打开文件
    int open_fd=open(addr1,O_RDONLY);
    if(open_fd == -1) {
        printf("open file failed: %s\n", addr1);
        free(filename);
        return -1;
    }

    char buf_read[4096];
    while(1){
        bzero(buf_read,sizeof(buf_read));
        int read_num=read(open_fd,buf_read,sizeof(buf_read));
        if(read_num<=0){
            break;
        }
        int send_file=send(socked_fd,buf_read,read_num,MSG_NOSIGNAL);
        ERROR_CHECK(send_file,-1," send file failed ");
    }

    close(open_fd);
    free(filename);
    printf("上传任务完成！\n");
    return 0;
}

int client_puts_mmap( ResponsePackage *presponse, ClientVirtualPathStack* pvirtualpathstack,int socked_fd){
    
    char* filename = (char*)calloc(256,1);

    char *addr1=strtok(presponse->args_val," ");//客户端文件地址

    int index=-1;
    for(int i=strlen(addr1)-1;i>=0;i--){//用此循环得到最后一个/的位置
        if(addr1[i]=='/'){
            index=i;
            break;
        }
    }
    if(index!=-1)
    {
        memcpy(filename, &addr1[index+1], strlen(addr1)-index-1);//修复：添加&取地址操作符
    }
    else
    {
        index=0;
        memcpy(filename, &addr1[index], strlen(addr1)-index);//修复：添加&取地址操作符
    }
    int filename_len=strlen(filename);
    // printf("addr1:%s filename:%s filename_len:%d\n",addr1,filename,filename_len);

    //发送文件名长度与文件名
    send(socked_fd,&filename_len,sizeof(int),MSG_NOSIGNAL);
    // printf("send filename_len end, %d\n",filename_len);
    send(socked_fd,filename,filename_len,MSG_NOSIGNAL);
    // printf("send filename end, %s\n",filename);

    //发送filesize
    struct stat file_stat;
    if (stat(addr1, &file_stat) == -1) {
        printf("stat error\n");
        return -1;
    }
    off_t filesize = file_stat.st_size;
    send(socked_fd,&filesize,sizeof(off_t),MSG_NOSIGNAL);
    // printf("send filesize end , %ld\n",filesize);

    int recvret =0;
    //等待服务端发送res_stat res_offset
    int res_stat = 0;
    off_t res_offset = 0;
    recvret = recvn(socked_fd,&res_stat,sizeof(res_stat),NULL);
    if(recvret == -1)
    {
        free(filename);
        printf("client recv res_stat error\n");
        return -1;
    }
    
    recvret = recvn(socked_fd,&res_offset,sizeof(res_offset),NULL);
    if(recvret==-1)
    {
        free(filename);
        printf("client recv res_offset error\n");
    }

    int ack_mode = 2;
    if(res_stat == 0)
    {
        send(socked_fd,&ack_mode,sizeof(int),MSG_NOSIGNAL);
        //send(socked_fd,&filesize,sizeof(off_t),MSG_NOSIGNAL);
    }
    else
    {
        if(res_stat ==1)
        {
            printf("存在同名文件,疑似上次上传中断(%ld/%ld),是否续写?\n",res_offset,filesize);
        }
        else
        {
            printf("存在同名文件,是否继续?\n");
        }
        printf("请输入(0:取消 1:续传 2:覆写):");
        fflush(stdout);

        char op=0;
        scanf("%c",&op);
        if(op=='0')
        {
            ack_mode = 0;
            send(socked_fd,&ack_mode,sizeof(int),MSG_NOSIGNAL);
            free(filename);
            printf("client cancle puts\n");
            return 0;
        }
        else if(op=='1')
        {
            ack_mode = 1;
            send(socked_fd,&ack_mode,sizeof(int),MSG_NOSIGNAL);
        }
        else
        {
            ack_mode = 2;
            res_offset = 0;
            send(socked_fd,&ack_mode,sizeof(int),MSG_NOSIGNAL);
        }
    }
    //打开文件
    int open_fd=open(addr1,O_RDWR);
    if(open_fd == -1) {
        printf("open file failed: %s\n", addr1);
        free(filename);
        return -1;
    }

    if(ack_mode == 1)
    {
        lseek(open_fd,res_offset,SEEK_SET);
    }
    else
    {
        lseek(open_fd,0,SEEK_SET);
    }

    long processed = res_offset;
    // long max_size = (long)512*1024*1024*1024;
    long max_size = (long)512*1024*1024;//512M
    while(processed<filesize)
    {
        long sendsize = filesize - processed > (max_size)?max_size:filesize - processed;
        char* p = (char*)mmap(NULL,sendsize,PROT_READ|PROT_WRITE,MAP_SHARED,open_fd,processed);
        //send(netfd,p,sendsize,MSG_NOSIGNAL);
        //printf("sendsize:%ld\n",sendsize);
        send(socked_fd,(void*)p,sendsize,MSG_NOSIGNAL);
        munmap(p,sendsize);
        processed+=sendsize;
        // printf("%ld/%ld\n",processed,filesize);
    }

    close(open_fd);
    free(filename);
    printf("上传任务执行完毕！");

    return 0;
}

int client_gets(ResponsePackage *presponse,const ClientVirtualPathStack* pvirtualpathstack,int socked_fd){
    
    char *addr1=strtok(presponse->args_val," ");//服务端文件的地址
    char *addr2=strtok(NULL," ");//客户端要存储到的文件夹的地址

    // printf("addr1: %s   addr2:%s\n",addr1,addr2);

    int filename_len = 0;
    char filename[256]={0};

    //接收filename
    int recvret = recvn(socked_fd,&filename_len,sizeof(int),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //接收filename_len
    recvret = recvn(socked_fd,filename,filename_len,NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //拼接将要下载到本地的文件的真实路径
    char localFilePath[1024]={0};
    sprintf(localFilePath,"%s/%s",addr2,filename);

    //文件大小
    off_t filesize = 0;
    recvn(socked_fd,&filesize,sizeof(off_t),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    // printf("filename:%s filename_len:%d filesize:%ld\n",filename,filename_len,filesize);
    // printf("localFilePath: %s\n",localFilePath);
    
    //创建文件
    int open_fd=open(localFilePath,O_WRONLY|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(open_fd,-1,"open file failed");

    char buff_write[4096]={0};
    off_t processed = 0;
    off_t recvsize = 0;
    while(1)
    {
        recvsize = 0;
        bzero(buff_write,sizeof(buff_write));
        // printf("%ld-%ld/%ld\n",recvsize,processed,filesize);
        recvsize = read(socked_fd,buff_write,sizeof(buff_write));
        processed+=recvsize;
        if(recvsize == 0)
        {
            // printf("client  puts transfile abort %ld-%ld\n",filesize,processed);
            ftruncate(open_fd,processed);
            close(open_fd);
            return -1;
        }
        write(open_fd,buff_write,recvsize);
        if(processed>=filesize)
        {
            break;
        }
    }

    close(open_fd); 
    printf("下载任务完成！\n");

    return 0;
}

int client_gets_mmap(ResponsePackage *presponse,const ClientVirtualPathStack* pvirtualpathstack,int socked_fd){
    
    char *addr1=strtok(presponse->args_val," ");//服务端文件的地址
    char *addr2=strtok(NULL," ");//客户端要存储到的文件夹的地址

    // printf("addr1: %s   addr2:%s\n",addr1,addr2);

    int filename_len = 0;
    char filename[256]={0};

    //接收filename
    int recvret = recvn(socked_fd,&filename_len,sizeof(int),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //接收filename_len
    recvret = recvn(socked_fd,filename,filename_len,NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //拼接将要下载到本地的文件的真实路径
    char localFilePath[1024]={0};
    sprintf(localFilePath,"%s/%s",addr2,filename);

    //文件大小
    off_t filesize = 0;
    recvn(socked_fd,&filesize,sizeof(off_t),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    // printf("filename:%s filename_len:%d filesize:%ld\n",filename,filename_len,filesize);
    // printf("localFilePath: %s\n",localFilePath);
    
    //创建文件前先判断,指定路径下是否有同名文件
    int open_fd =0;
    off_t samename_filesize =0;
    int ack_mode = 2;
    if(access(localFilePath,F_OK)==0)
    {
        //有同名文件
        struct stat samename_file_stat;
        stat(localFilePath,&samename_file_stat);
        samename_filesize = samename_file_stat.st_size;

        samename_filesize = samename_filesize - (samename_filesize %4096);
        printf("指定路径下已有同名文件，是否继续？%ld\n",samename_filesize);
        printf("请输入(0:取消 1:续写 2:覆写 ):");
        fflush(stdout);
        char userop = 0 ;
        scanf("%c",&userop);
        if(userop == '0')
        {
            ack_mode = 0;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            return -1;
        }
        else if(userop == '1')
        {
            ack_mode = 1;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            open_fd = open(localFilePath,O_RDWR |O_APPEND);
            lseek(open_fd,samename_filesize,SEEK_SET);
        }
        else
        {
            ack_mode = 2;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            open_fd = open(localFilePath,O_RDWR|O_TRUNC);
        }
    }
    else
    {
        ack_mode = 2;
        send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
        send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
        open_fd = open(localFilePath,O_CREAT|O_RDWR,0666);
    }

    ftruncate(open_fd,filesize);
    long processed = ack_mode==1?samename_filesize:0;
    long max_size = (long)512*1024*1024;
    while(processed<filesize)
    {
        long recvsize = filesize-processed>max_size?max_size:filesize-processed;
        char* p = (char*)mmap(NULL,recvsize,PROT_READ|PROT_WRITE,MAP_SHARED,open_fd,processed);
        long recvBytes = 0;

        recvret = recvn(socked_fd,p,recvsize,&recvBytes);
        processed+=recvBytes;
        if(recvret==-1)
        {
            printf("client gets abort\n");
            ftruncate(open_fd,processed);
            close(open_fd);
            munmap(p,recvsize);
            return -1;
        }
        munmap(p,recvsize);
    }

    close(open_fd); 
    printf("下载任务完成！\n");

    return 0;
}
int client_gets_mmap2(ResponsePackage *presponse,const ClientVirtualPathStack* pvirtualpathstack,int socked_fd){
    
    char *addr1=strtok(presponse->args_val," ");//服务端文件的地址
    char *addr2=strtok(NULL," ");//客户端要存储到的文件夹的地址

    // printf("addr1: %s   addr2:%s\n",addr1,addr2);

    int filename_len = 0;
    char filename[256]={0};

    //接收filename
    int recvret = recvn(socked_fd,&filename_len,sizeof(int),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //接收filename_len
    recvret = recvn(socked_fd,filename,filename_len,NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    //拼接将要下载到本地的文件的真实路径
    char localFilePath[1024]={0};
    sprintf(localFilePath,"%s/%s",addr2,filename);

    //文件大小
    off_t filesize = 0;
    recvn(socked_fd,&filesize,sizeof(off_t),NULL);
    if(recvret==-1){
        printf("server gets transfile abort");
        return -1;
    }

    // printf("filename:%s filename_len:%d filesize:%ld\n",filename,filename_len,filesize);
    // printf("localFilePath: %s\n",localFilePath);
    
    //创建文件前先判断,指定路径下是否有同名文件
    int open_fd =0;
    off_t samename_filesize =0;
    int ack_mode = 2;
    if(access(localFilePath,F_OK)==0)
    {
        //有同名文件
        struct stat samename_file_stat;
        stat(localFilePath,&samename_file_stat);
        samename_filesize = samename_file_stat.st_size;

        samename_filesize = samename_filesize - (samename_filesize %4096);
        printf("指定路径下已有同名文件，是否继续？%ld\n",samename_filesize);
        printf("请输入(0:取消 1:续写 2:覆写 ):");
        fflush(stdout);
        char userop = 0 ;
        scanf("%c",&userop);
        if(userop == '0')
        {
            ack_mode = 0;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            return -1;
        }
        else if(userop == '1')
        {
            ack_mode = 1;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            open_fd = open(localFilePath,O_RDWR);
            lseek(open_fd,samename_filesize,SEEK_SET);
        }
        else
        {
            ack_mode = 2;
            send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
            send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
            open_fd = open(localFilePath,O_RDWR|O_TRUNC);
        }
    }
    else
    {
        ack_mode = 2;
        send(socked_fd,&ack_mode,sizeof(ack_mode),MSG_NOSIGNAL);
        send(socked_fd,&samename_filesize,sizeof(off_t),MSG_NOSIGNAL);
        open_fd = open(localFilePath,O_CREAT|O_RDWR,0666);
    }

    //ftruncate(open_fd,filesize);
    long processed = ack_mode==1?samename_filesize:0;
    //long max_size = (long)512*1024*1024;
    char recvbuf[4096]={0};
    while(processed<filesize)
    {
        bzero(recvbuf,4096);
        int recvsize = recv(socked_fd,recvbuf,4096,0);
        if(recvsize ==0)
        {
            break;
        }
        write(open_fd,recvbuf,recvsize);
        processed+=recvsize;
    }
    close(open_fd); 
    printf("下载任务完成！\n");

    return 0;
}
int send_TLVLV_req(RequestPackage *prequst,const int socked_fd){

    //发送标志位
    int send_tag_ret=send(socked_fd,&prequst->type,sizeof(int),MSG_NOSIGNAL);
    ERROR_CHECK(send_tag_ret,-1,"send tag fail");

    //发送虚拟路径的长度
    int send_vir_path_len_ret=send(socked_fd,&prequst->vir_path_len,sizeof(int),MSG_NOSIGNAL);
    ERROR_CHECK(send_vir_path_len_ret,-1,"send vir path len failed");

    //发送虚拟路径的内容
    int send_vir_path_val_ret=send(socked_fd,prequst->vir_path_val,prequst->vir_path_len,MSG_NOSIGNAL);
    ERROR_CHECK(send_vir_path_val_ret,-1,"send vir path value failed");

    //发送参数长度
    int send_args_len_ret=send(socked_fd,&prequst->args_len,sizeof(int),MSG_NOSIGNAL);
    ERROR_CHECK(send_args_len_ret,-1,"send args len len failed");

    //发送参数的内容
    int send_args_val_ret=send(socked_fd,prequst->args_val,prequst->args_len,MSG_NOSIGNAL);
    ERROR_CHECK(send_args_val_ret,-1,"send args value failed");    
    
    return 0;
}
int recv_TLVLV_req(ResponsePackage *presponse,const int socked_fd){

    //接受标志位
    int type;
    int recv_tag_ret = recv(socked_fd, &type, sizeof(int), MSG_WAITALL);
    ERROR_CHECK(recv_tag_ret,-1,"recv tag fail");
    presponse->type = (MSG_TYPE)type;

    //接受虚拟路径的长度
    int vir_path_len;
    int recv_vir_path_len_ret = recv(socked_fd, &vir_path_len, sizeof(int), MSG_WAITALL);
    ERROR_CHECK(recv_vir_path_len_ret,-1,"recv vir path len failed");
    presponse->vir_path_len = vir_path_len;

    //接受虚拟路径的内容
    char buf_vir_path_val[1024]={0};
    int recv_vir_path_val_ret=recv(socked_fd,buf_vir_path_val,presponse->vir_path_len,MSG_WAITALL);
    ERROR_CHECK(recv_vir_path_val_ret,-1,"recv vir path value failed");
    memcpy(presponse->vir_path_val, buf_vir_path_val, vir_path_len);

    //接受参数长度
    int args_len;
    int recv_args_len_ret = recv(socked_fd, &args_len, sizeof(int), MSG_WAITALL);
    ERROR_CHECK(recv_args_len_ret,-1,"recv args len len failed");
    presponse->args_len = args_len;

    //接受参数的内容
    char buf_args_val[1024]={0};
    int recv_args_val_ret=recv(socked_fd,buf_args_val,presponse->args_len,MSG_WAITALL);
    ERROR_CHECK(recv_args_val_ret,-1,"recv args value failed");    
    memcpy(presponse->args_val,buf_args_val,presponse->args_len);
    
    return 0;
}

